#include "Entity.h"

#include "depricated/EntityComponent.h"
#include "global_script_functions.h"
#include "physics/PhysicsManager.h"
#include "AnimationLogic.h"
#include "EntityManager.h"
#include "KeyboardEvent.h"
#include "Logger.h"
#include "Property.h"
#include "PhysicsLogic.h"
#include "RenderingManager.h"
#include "ScriptLogic.h"
#include "Utilities.h"
#include "StateStack.h"
#include "ServiceLocator.h"
#include "State.h"

namespace EntitySystem
{

	using namespace components;

	Entity::Entity() : alive(true), position(Math::Vector2F::Zero)
	{
		physicsLogic = NULL;
		animationLogic = NULL;
	}

	/**
	* The copy constructor is highly used. All entities are built utilizing the prototype
	* pattern. So each entity viewed in the game world is a clone of a prototype
	* held by entity manager.
	* @param org the original prototype to be copied.
	*/

	Entity::Entity(Entity& org)
		:
	type(org.type),
		category(org.category),
		alive(true), position(Math::Vector2F::Zero)
	{
		animationLogic = NULL;
		physicsLogic = NULL;

		if( org.animationLogic != NULL )
		{
			SetAnimationLogic( org.animationLogic->Clone( ) );
		}

		if( org.physicsLogic != NULL )
		{
			SetPhysicsLogic( org.physicsLogic->Clone( ) );
		}

		//copy each property
		PropertyMap::iterator iter( org.m_properties.begin( ) ), end( org.m_properties.end( ) );
		for(; iter != end; ++iter )
		{
			Property *p = new Property( *( iter->second ) );

			m_properties.insert( m_properties.end( ), std::pair< std::string, Property* >( iter->first, p ) );
		}

		//for each script logic of the original
		unsigned scriptCount = org.scripts.size( );
		scripts.reserve( scriptCount );
		for( unsigned i = 0; i < scriptCount; ++i )
		{
			ScriptLogic *toAdd = org.scripts[i]->Clone( );
			toAdd->SetParent( this );
			scripts.push_back( toAdd );
		}
	}

	/**
	* For each different logical component of entity, this destructor must first remove it
	* from its manager and the deallocate it.
	*/
	Entity::~Entity()
	{
		delete physicsLogic;
		delete animationLogic;

		//free each script logic object
		std::for_each( scripts.begin( ), scripts.end( ), FreePointer< ScriptLogic > ( ) );
		//free properties
		PropertyMap::iterator iter( m_properties.begin( ) ), end( m_properties.end( ) );

		for(; iter != end; ++iter )
		{
			delete iter->second;
		}
	}

	void Entity::Update()
	{
		//update all scripts
		ScriptPredicates::ScriptExecutorOnUpdate update;
		ExecuteOnScripts( update );
	}

	/**
	* Adds x,y to the postion vector.
	* @param x to be added to position.x
	* @param y to be added to position.y
	*/
	void Entity::AdjustPosition(float x, float y)
	{
		position.Move( x, y );
	}

	/**
	* Sends a string message to current state along with this entity.
	* 
	* @param msg the msg that will be send to current state
	*/

	void Entity::NotifyState(const std::string &msg)
	{
		StateStack *stateStack = ServiceLocator< StateStack >::GetService( );

		State *currState = stateStack->Top( );

		currState->OnEntityMessage( this, msg );
	}

	/*depricated
	void Entity::AddComponent(EntityComponent* comp)
	{
	comp->SetParent( this );
	componentList.push_back( comp );
	}


	void Entity::RemoveComponent(ENTCOMP_TYPES type)
	{
	std::list<EntityComponent*>::iterator it;
	for( it = componentList.begin( ); it != componentList.end( ); ++it )
	{
	if( ( *it )->GetType( ) == type )
	{
	EntityComponent* comp = *it;
	componentList.erase( it );
	delete comp;
	//if components are uniqe return;
	return;
	}
	}
	}


	EntityComponent* Entity::GetComponent(ENTCOMP_TYPES type)
	{
	std::list<EntityComponent*>::iterator it;
	for( it = componentList.begin( ); it != componentList.end( ); ++it )
	{
	if( ( *it )->GetType( ) == type ) return *it;
	}
	return NULL;
	}
	*/

	void Entity::SetCategory(const std::string& category)
	{
		this->category = category;
	}

	std::string Entity::GetCategory() const
	{
		return category;
	}

	void Entity::SetType(const std::string& scriptType)
	{
		this->type = scriptType;
	}

	std::string Entity::GetType() const
	{
		return type;
	}

	bool Entity::IsAlive()
	{
		return alive;
	}

	void Entity::Kill()
	{
		alive = false;
		//kill all scripts
		ScriptPredicates::ScriptExecutorOnDeath death;
		ExecuteOnScripts( death );
	}

	void Entity::SetID(const std::string& ID)
	{
		this->ID = ID;
	}

	void Entity::SetID(const char* ID)
	{
		this->ID = ID;
	}

	std::string Entity::GetID() const
	{
		return ID;
	}

	Math::Vector2F Entity::GetPosition()
	{
		return position;
	}

	void Entity::SetPosition(const Math::Vector2F& pos)
	{
		position.Set( pos );
	}

	void Entity::SetPosition(float x, float y)
	{
		position.Set( x, y );
	}

	float Entity::GetPositionX()
	{
		return position.GetX( );
	}

	float Entity::GetPositionY()
	{
		return position.GetY( );
	}

	void Entity::SetAlive(bool alive)
	{
		this->alive = alive;
	}

	/**
	* Returns the width of the entity's draw logic. If the entity has no draw
	* logic returns 0.
	*/

	int Entity::GetWidth() const
	{
		return animationLogic != NULL ? animationLogic->GetWidth( ) : 0;
	}

	/**
	* Returns the height of the entity's draw logic. If the entity has no draw
	* logic returns 0.
	*/

	int Entity::GetHeight() const
	{
		return animationLogic != NULL ? animationLogic->GetHeight( ) : 0;
	}

	void Entity::SetPhysicsLogic(PhysicsLogic* physicsLogic)
	{
		if( this->physicsLogic != NULL )
		{
			delete this->physicsLogic;
		}

		this->physicsLogic = physicsLogic;
		physicsLogic->SetParent( this );
	}

	PhysicsLogic* Entity::GetPhysicsLogic() const
	{
		return physicsLogic;
	}

	void Entity::SetAnimationLogic(AnimationLogic* animationLogic)
	{
		if( this->animationLogic != NULL )
		{
			delete this->animationLogic;
		}

		this->animationLogic = animationLogic;
		this->animationLogic->SetParent( this );
	}

	AnimationLogic* Entity::GetAnimationLogic() const
	{
		return animationLogic;
	}

	/**
	* Sets the script object of this Entity
	*
	* @param scriptObject - the script object that controls the entity
	*/

	void Entity::AddScriptLogic(ScriptLogic *scriptObject)
	{
		//if a script of this type isn't already attached to this entity
		//if(FindScript(obj->GetObjectType()->GetName()) == -1)
		{
			scriptObject->SetParent( this );
			scripts.push_back( scriptObject );
		}
	}

	/**
	* Removes a script object attached to this Entity by specifying its type.
	*
	* @param scriptObjectType - the type of the script object
	*/

	void Entity::RemoveScriptLogic(const std::string &scriptObjectType)
	{
		//search for the script object
		int scriptObjectIndex = FindScript( scriptObjectType );
		//if the script object exists
		if( scriptObjectIndex >= 0 )
		{
			delete scripts[scriptObjectIndex];
			scripts.erase( scripts.begin( ) + scriptObjectIndex );
		}
	}

	/**
	* Calls the Initialize method for every script attached to this Entity
	*/

	void Entity::InitializeScripts()
	{
		ScriptPredicates::ScriptExecutorOnInit init;
		ExecuteOnScripts( init );
	}

	/**
	* Returns a copy of the internal vector storing the scripts attached to
	* the Entity.
	*
	* @param out: OUTPUT parameter, internal vector is copied here.
	*/

	void Entity::GetScripts(std::vector< ScriptLogic* > &out) const
	{
		out.assign( scripts.begin( ), scripts.end( ) );
	}

	/**
	* Informs Entity of a keyboard input event.
	* 
	* @param event: the event that we want the Entity to be aware of.
	*/

	void Entity::PushKeyboardInput(const KeyboardEvent &event, std::string& association)
	{
		if( event.GetType( ) == KEY_RELEASED )
		{
			ScriptPredicates::ScriptExecutorOnKeyUp up( event, association );
			ExecuteOnScripts( up );
		}
		else if( event.GetType( ) == KEY_PRESSED )
		{
			ScriptPredicates::ScriptExecutorOnKeyDown down( event, association );
			ExecuteOnScripts( down );
		}
	}

	/**
	* Creates a property, adds it to this Entity and returns a pointer to it.
	*
	* @param name: name of the property.
	* @return a read-write handle to the property.
	*/

	Property* Entity::AddProperty(const std::string& name)
	{
		Property* property = GetProperty( name );

		if( property == NULL )
		{
			property = new Property;

			m_properties.insert( m_properties.end( ), std::pair< std::string, Property* >( name, property ) );
		}

		return property;
	}

	/**
	* Returns a property of this Entity based on its name.
	*
	* @param name: name of the property to find.
	* @return a read-write handle to the requested property.
	* @retval NULL: if the property doesn't exist.
	*/

	Property* Entity::GetProperty(const std::string& name)
	{
		PropertyMap::iterator result( m_properties.find( name ) );

		return result != m_properties.end( ) ? result->second : NULL;
	}

	/**
	* Finds a script attached to this Entity by the script object's type.
	*
	* @param scriptObjectType - the type of the script object.
	* @return an integer value indicating the index of the script.
	* @retval -1: if script doesn't exist
	*/

	int Entity::FindScript(const std::string &scriptObjectType)
	{
		unsigned scriptCount = scripts.size( );
		int pos = -1;
		for( unsigned i = 0; i < scriptCount; ++i )
		{
			if( scriptObjectType == scripts[i]->GetName( ) )
			{
				pos = static_cast < int > ( i );
			}
		}

		return pos;
	}


};
